using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using System.Xml;

namespace Woz
{
    /// <summary>
    /// group class used by the select tool
    /// </summary>
    /// <remarks>you must add entities to the group and calculate the bounding box before usage</remarks>
    internal class Group : Woz.Entity, Woz.IEntityContainer
    {
        /// <summary>
        /// creates a new group using the xml stream and path
        /// </summary>
        /// <param name="pStream">the xml stream to load the group data from</param>
        /// <param name="pBoundingBox">the boundin box of the entity</param>
        /// <param name="pParams">the path of the xml stream</param>
        public Group(XmlReader pStream, Rectangle pBoundingBox, string pPath)
            : base(pBoundingBox.Location, pBoundingBox.Size)
        {
            List<uint> loadedEntities = new List<uint>();

            // load entity from xml
            pStream.ReadStartElement("Group", "urn:Woz");

            while (pStream.IsStartElement("Entity", "urn:Woz"))
            {
                loadedEntities.Add(uint.Parse(pStream.GetAttribute("ID")));
                pStream.ReadElementString("Entity", "urn:Woz");
            }

            pStream.ReadEndElement();

            // initialize the entity list
            this.mEntities = Woz.ScreenManager.Instance.FindLoadedEntities(loadedEntities.ToArray());

            // compute the bounding box
            this.mBoundingBox = Woz.Entity.ComputeBoundingBox(this.mEntities);
        }

        /// <summary>
        /// group class used to group entities into one group
        /// </summary>
        public Group(Woz.Entity[] pEntities) : base(Point.Empty, Size.Empty)
        {
            // initialize the entities list
            this.mEntities = pEntities;
            
            // compute the bounding box
            this.mBoundingBox = Woz.Entity.ComputeBoundingBox(this.mEntities);
        }

        /// <summary>
        /// gets the entities of this group
        /// </summary>
        public Woz.Entity[] Entities
        {
            get
            {
                return this.mEntities;
            }
        }

        /// <summary>
        /// figure out whether or not entity lies within
        /// given pair of points 
        /// </summary>
        /// <param name="pX">x position to check</param>
        /// <param name="pY">y position to check</param>
        /// <returns>true if entity lies within pX, pY</returns>
        public override bool HitTest(int pX, int pY)
        {
            // go through all of the entities in the group
            foreach (Woz.Entity entity in this.mEntities)
            {
                // check if any member of the group was hit
                if (entity.HitTest(pX, pY))
                {
                    return true;
                }
            }

            // if nobody was hit the group was not hit
            return false;
        }

        /// <summary>
        /// tells entity to draw itself using the graphics object
        /// </summary>
        /// <param name="pGraphics">the graphics object to use</param>
        public override void Draw(Graphics pGraphics)
        {
            foreach (Woz.Entity entity in this.mEntities)
            {
                entity.Draw(pGraphics);
            }
        }

        /// <summary>
        /// dispose this entity
        /// </summary>
        public override void Dispose()
        {
        }

        /// <summary>
        /// create an exact copy of this entity and return it
        /// </summary>
        /// <returns>an exact duplicate of this entity</returns>
        public override object Clone()
        {
            // clone the entities
            Woz.Entity[] entities = new Woz.Entity[this.mEntities.Length];
            for (int i = 0; i < this.mEntities.Length; i++)
            {
                entities[i] = this.mEntities[i].Clone() as Woz.Entity;
            }

            // return a copy of the group
            return new Woz.Group(entities);
        }

        /// <summary>
        /// checks whether or not this entity supports an attribute
        /// </summary>
        /// <param name="pAttribute">the name of the attribute</param>
        /// <returns>true if attribute is supported</returns>
        public override bool SupportsAttribute(string pAttribute)
        {
            // check if any entity in the group supports this attribute
            foreach (Woz.Entity entity in this.mEntities)
            {
                if (entity.SupportsAttribute(pAttribute))
                {
                    return true;
                }
            }

            // if all else fails just return false
            return false;
        }

        /// <summary>
        /// gets a reference to the attribute with the given attribute name
        /// </summary>
        /// <param name="pAttribute">the name of the attribute</param>
        /// <returns>a reference to the attribute</returns>
        public override object GetAttribute(string pAttribute)
        {
            // get the first entity with this attribute
            object att;
            foreach (Woz.Entity entity in this.mEntities)
            {
                att = entity.GetAttribute(pAttribute);
                if (att != null)
                {
                    return att;
                }
            }

            // if nothing else is found return null
            return null;
        }

        /// <summary>
        /// saves a reference to the attribute with the given attribute name
        /// </summary>
        /// <param name="pAttribute">the name of the attribute</param>
        /// <param name="pObject">a reference to the attribute</param>
        public override void SetAttribute(string pAttribute, object pObject)
        {
            // set this attribute to all entities
            foreach (Woz.Entity entity in this.mEntities)
            {
                entity.SetAttribute(pAttribute, pObject);
            }
        }

        /// <summary>
        /// returns a string representing the group
        /// </summary>
        /// <returns>a string representing the group</returns>
        public override string ToString()
        {
            return String.Format("Group of {0} Entities", this.mEntities.Length);
        }

        /// <summary>
        /// the actual moving of the entity happens here
        /// </summary>
        /// <param name="pDeltaX">the relative x amount to move entity by</param>
        /// <param name="pDeltaY">the relative y amount to move entity by</param>
        protected override void OnMove(int pDeltaX, int pDeltaY)
        {
            // move the whole group
            foreach (Woz.Entity entity in this.mEntities)
            {
                entity.Translate(pDeltaX, pDeltaY);
            }

            // now adjust the bounding box
            this.mBoundingBox.X += pDeltaX;
            this.mBoundingBox.Y += pDeltaY;
        }

        /// <summary>
        /// the actual scaling of the entity happens here
        /// </summary>
        /// <param name="pDeltaWidth">the relative amount of width</param>
        /// <param name="pDeltaHeight">the relative amount of height</param>
        protected override void OnScale(int pDeltaWidth, int pDeltaHeight)
        {
            // scale the whole group
            foreach (Woz.Entity entity in this.mEntities)
            {
                entity.Scale(pDeltaWidth, pDeltaHeight);
            }

            // update the bounding box
            this.mBoundingBox.Width += pDeltaWidth;
            this.mBoundingBox.Height += pDeltaHeight;
        }

        /// <summary>
        /// the actual saving of the entity happens here
        /// </summary>
        /// <param name="pStream">the stream to save to</param>
        /// <param name="pPath">the path of the temporary files</param>
        protected override void OnSave(XmlWriter pStream, string pPath)
        {
            pStream.WriteStartElement("Group", "urn:Woz");
            foreach (Woz.Entity entity in this.mEntities)
            {
                pStream.WriteStartElement("Entity", "urn:Woz");
                pStream.WriteAttributeString("ID", entity.ID.ToString());
                pStream.WriteEndElement();
            }
            pStream.WriteEndElement();
        }

        /// <summary>
        /// the entities of this group
        /// </summary>
        private Woz.Entity[] mEntities;
    }
}
